package org.scboy.common.gen;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.scboy.common.gen.core.Gen;
import org.scboy.common.gen.core.Root;
import org.scboy.coobi.scan.FileScan;
import org.scboy.utils.entitys.coobi.DbTable;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

public class GenClass {
	
	public static Log log=LogFactory.get();
	
	/**
	 * 在项目根包下面创建App类。在main方法中调动create方法。
	 * 使用类生成，需要先创建项目所需要的表配置文件.entity.xml。
	 * 注意：生成类时，如果文件已经存在，会替换相关文件。
	 * 在老项目建议生成在test目录。生成后复制到main目录。
	 * @param clazz 调用类实例
	 * @param systemName 系统中文名称
	 * @param type 文件存放资源类型，MAIN放入java/main目录.TEST放入java/test目录
	 * @param tables 固定生成类，为null生成所有类
	 */
	public static void createDao(Class<?> clazz,ResouseType type,String ... tables) {
		//获取类路径
		String objClassRoot="";
		try {
			objClassRoot = clazz.getResource("/").toURI().getPath().toString();
		} catch (URISyntaxException e1) {
			log.error(e1);
		}
		//包路径
		String pk=clazz.getPackage().getName();
		//项目名称
		String objName=pk.substring(pk.lastIndexOf(".")+1);
		//生成路径
		String objRoot=objClassRoot.substring(0,objClassRoot.lastIndexOf("target/classes/"));
		String resource=objRoot+"src/main/resources/";
		Root.RES=objRoot+"src/main/resources/"+objName+"/";
		Root.SRC=objRoot+"src/main/java/"+pk.replaceAll("\\.", "/")+"/";
		Root.TEST_RES=objRoot+"src/test/resources/"+objName+"/";
		Root.TEST_SRC=objRoot+"src/test/java/"+pk.replaceAll("\\.", "/")+"/";
		//扫描项目下面所有的.entity.xml配置
		log.info(Root.RES);
		log.info(Root.SRC);
		log.info(Root.TEST_RES);
		log.info(Root.TEST_SRC);
		List<DbTable> entitys=new ArrayList<DbTable>();
		try {
			scan(new File(resource), entitys);
		} catch (FileNotFoundException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		}

		if(entitys != null && entitys.size() > 0) {
			for (DbTable table : entitys) {
				String [] arrayName=table.getName().split("_");
				String tas="";
				for (String n : arrayName) {
					if(n.length()>1) {
						tas+=n.substring(0,1).toUpperCase(Locale.ENGLISH)+n.substring(1).toLowerCase(Locale.ENGLISH);
					}else {
						tas+=n.toUpperCase(Locale.ENGLISH);
					}
				}
				Map<String, Object> data=new HashMap<String, Object>();
				data.put("fileName", tas+"Dao.java");
				data.put("className", tas+"Dao");
				data.put("pk", pk);
				data.put("entity", table);
				data.put("tas", tas);
				if(type==ResouseType.MAIN) {
					File file=new File(Root.SRC+"dao/"+tas+"Dao.java");
					if(!file.exists()) {
						data.put("path", Root.SRC+"dao");
					}else {
						log.warn(tas+"Dao.java 已经存在。");
						continue;
					}
				}else {
					File file=new File(Root.TEST_SRC+"dao/"+tas+"Dao.java");
					if(!file.exists()) {
						data.put("path", Root.TEST_SRC+"dao");
					}else {
						log.warn(tas+"Dao.java 已经存在。");
						continue;
					}
				}
				new Gen("dao.btl", data);
			}
		}
	}
	
	public static void createService(Class<?> clazz,ResouseType type,String ... tables) {
		//获取类路径
		String objClassRoot="";
		try {
			objClassRoot = clazz.getResource("/").toURI().getPath().toString();
		} catch (URISyntaxException e1) {
			log.error(e1);
		}
		//包路径
		String pk=clazz.getPackage().getName();
		//项目名称
		String objName=pk.substring(pk.lastIndexOf(".")+1);
		//生成路径
		String objRoot=objClassRoot.substring(0,objClassRoot.lastIndexOf("target/classes/"));
		String resource=objRoot+"src/main/resources/";
		Root.RES=objRoot+"src/main/resources/"+objName+"/";
		Root.SRC=objRoot+"src/main/java/"+pk.replaceAll("\\.", "/")+"/";
		Root.TEST_RES=objRoot+"src/test/resources/"+objName+"/";
		Root.TEST_SRC=objRoot+"src/test/java/"+pk.replaceAll("\\.", "/")+"/";
		//扫描项目下面所有的.entity.xml配置
		log.info(Root.RES);
		log.info(Root.SRC);
		log.info(Root.TEST_RES);
		log.info(Root.TEST_SRC);
		List<DbTable> entitys=new ArrayList<DbTable>();
		try {
			scan(new File(resource), entitys);
		} catch (FileNotFoundException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		}

		if(entitys != null && entitys.size() > 0) {
			for (DbTable table : entitys) {
				String [] arrayName=table.getName().split("_");
				String tas="";
				for (String n : arrayName) {
					if(n.length()>1) {
						tas+=n.substring(0,1).toUpperCase(Locale.ENGLISH)+n.substring(1).toLowerCase(Locale.ENGLISH);
					}else {
						tas+=n.toUpperCase(Locale.ENGLISH);
					}
				}
				
				Map<String, Object> service=new HashMap<String, Object>();
				service.put("fileName", tas+"Service.java");
				service.put("className", tas+"Service");
				service.put("pk", pk);
				service.put("entity", table);
				service.put("tas", tas);
				if(type==ResouseType.MAIN) {
					File file=new File(Root.SRC+"service/"+tas+"Service.java");
					if(!file.exists()) {
						service.put("path", Root.SRC+"service");
					}else {
						log.warn(tas+"Service.java 已经存在。");
					}
				}else {
					File file=new File(Root.TEST_SRC+"service/"+tas+"Service.java");
					if(!file.exists()) {
						service.put("path", Root.TEST_SRC+"service");
					}else {
						log.warn(tas+"Service.java 已经存在。");
					}
				}
				new Gen("service.btl", service);
				
				
				Map<String, Object> serviceImpl=new HashMap<String, Object>();
				serviceImpl.put("fileName", tas+"ServiceImpl.java");
				serviceImpl.put("className", tas+"ServiceImpl");
				serviceImpl.put("pk", pk);
				serviceImpl.put("entity", table);
				serviceImpl.put("tas", tas);
				if(type==ResouseType.MAIN) {
					File file=new File(Root.SRC+"service/imp/"+tas+"ServiceImpl.java");
					if(!file.exists()) {
						serviceImpl.put("path", Root.SRC+"service/imp");
					}else {
						log.warn(tas+"ServiceImpl.java 已经存在。");
					}
				}else {
					File file=new File(Root.TEST_SRC+"service/imp/"+tas+"ServiceImpl.java");
					if(!file.exists()) {
						serviceImpl.put("path", Root.TEST_SRC+"service/imp");
					}else {
						log.warn(tas+"ServiceImpl.java 已经存在。");
					}
				}
				new Gen("serviceimp.btl", serviceImpl);
			}
		}
	}
	

	public static void createController(String systemName,Class<?> clazz,ResouseType type,String ... tables) {
		//获取类路径
		String objClassRoot="";
		try {
			objClassRoot = clazz.getResource("/").toURI().getPath().toString();
		} catch (URISyntaxException e1) {
			log.error(e1);
		}
		//包路径
		String pk=clazz.getPackage().getName();
		//项目名称
		String objName=pk.substring(pk.lastIndexOf(".")+1);
		//生成路径
		String objRoot=objClassRoot.substring(0,objClassRoot.lastIndexOf("target/classes/"));
		String resource=objRoot+"src/main/resources/";
		Root.RES=objRoot+"src/main/resources/"+objName+"/";
		Root.SRC=objRoot+"src/main/java/"+pk.replaceAll("\\.", "/")+"/";
		Root.TEST_RES=objRoot+"src/test/resources/"+objName+"/";
		Root.TEST_SRC=objRoot+"src/test/java/"+pk.replaceAll("\\.", "/")+"/";
		//扫描项目下面所有的.entity.xml配置
		log.info(Root.RES);
		log.info(Root.SRC);
		log.info(Root.TEST_RES);
		log.info(Root.TEST_SRC);
		List<DbTable> entitys=new ArrayList<DbTable>();
		try {
			scan(new File(resource), entitys);
		} catch (FileNotFoundException e) {
			log.error(e);
		} catch (IOException e) {
			log.error(e);
		}

		if(entitys != null && entitys.size() > 0) {
			for (DbTable table : entitys) {
				String [] arrayName=table.getName().split("_");
				String tas="";
				for (String n : arrayName) {
					if(n.length()>1) {
						tas+=n.substring(0,1).toUpperCase(Locale.ENGLISH)+n.substring(1).toLowerCase(Locale.ENGLISH);
					}else {
						tas+=n.toUpperCase(Locale.ENGLISH);
					}
				}
				
				Map<String, Object> controller=new HashMap<String, Object>();
				controller.put("fileName", tas+"Controller.java");
				controller.put("className", tas+"Controller");
				controller.put("pk", pk);
				controller.put("entity", table);
				controller.put("tas", tas);
				controller.put("objName", objName);
				controller.put("systemName", systemName);
				controller.put("simleEntityName", tas.toLowerCase(Locale.ENGLISH));
				
				if(type==ResouseType.MAIN) {
					File file=new File(Root.SRC+"controller/"+tas+"Controller.java");
					if(!file.exists()) {
						controller.put("path", Root.SRC+"controller");
					}else {
						log.warn(tas+"Service.java 已经存在。");
					}
				}else {
					File file=new File(Root.TEST_SRC+"controller/"+tas+"Controller.java");
					if(!file.exists()) {
						controller.put("path", Root.TEST_SRC+"controller");
					}else {
						log.warn(tas+"Controller.java 已经存在。");
					}
				}
				new Gen("controller.btl", controller);
				
			}
		}
	}
	
	/**
	 * 读取配置文件，加入到内存中。
	 * @param file
	 * @param alias
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void fileConfig(File file,List<DbTable> entitys) throws FileNotFoundException, IOException{
		String fname=file.getName();
		if (fname.toLowerCase(Locale.ENGLISH).endsWith(".entity.json")||fname.toLowerCase(Locale.ENGLISH).endsWith(".entity.xml")) {
			//
			//初始化表配置
			//读取sql配置文件，文件以.entity.json和.entity.xml
			//读取的数据放入内存中，在放入内存时，检查当前内存中配置版本是否低于最新读取的版本
			//如果内存版本低于最新读取到的配置版本，把内存表配置替换成最新读取的配置。
			//
			if(fname.toLowerCase(Locale.ENGLISH).endsWith(".entity.json")){
				//json方式
				String name=fname.replaceAll("\\.entity\\.json", "");
				log.info("扫描到表[JSON]["+name+"]配置,路径：["+file.getAbsolutePath()+"]");
				String charset=get_charset(new FileInputStream(file));
				entitys.add(FileScan.readTable(name, new FileInputStream(file), "json", charset));
			}else if(fname.toLowerCase(Locale.ENGLISH).endsWith(".entity.xml")){
				//xml方式
				String entityName=fname.replaceAll("\\.entity\\.xml", "");
				log.info("扫描到表[XML]["+entityName+"]配置,路径：["+file.getAbsolutePath()+"]");
				String charset=get_charset(new FileInputStream(file));
				entitys.add(FileScan.readTable(entityName, new FileInputStream(file), "xml", charset));
			}
		}
	}
	/**
	 * 获取文件编码
	 * @param inputStream
	 * @return
	 * @throws IOException
	 */
	public static String get_charset(InputStream inputStream) throws IOException {
		String charset = "GBK";
		byte[] first3Bytes = new byte[3];
		BufferedInputStream bis = null;
		try {
			boolean checked = false;
			bis = new BufferedInputStream(inputStream);
			bis.mark(0);
			int read = bis.read(first3Bytes, 0, 3);
			if (read == -1)
				return charset;
			if (first3Bytes[0] == (byte) 0xFF
					&& first3Bytes[1] == (byte) 0xFE) {
				charset = "UTF-16LE";
				checked = true;
			} else if (first3Bytes[0] == (byte) 0xFE
					&& first3Bytes[1] == (byte) 0xFF) {
				charset = "UTF-16BE";
				checked = true;
			} else if (first3Bytes[0] == (byte) 0xEF
					&& first3Bytes[1] == (byte) 0xBB
					&& first3Bytes[2] == (byte) 0xBF) {
				charset = "UTF-8";
				checked = true;
			}
			bis.reset();
			if (!checked) {
				while ((read = bis.read()) != -1) {
					if (read >= 0xF0)
						break;
					if (0x80 <= read && read <= 0xBF) // 单独出现BF以下的，也算是GBK
						break;
					if (0xC0 <= read && read <= 0xDF) {
						read = bis.read();
						if (0x80 <= read && read <= 0xBF) // 双字节 (0xC0 - 0xDF)
							// (0x80
							// - 0xBF),也可能在GB编码内
							continue;
						else
							break;
					} else if (0xE0 <= read && read <= 0xEF) {// 也有可能出错，但是几率较小
						read = bis.read();
						if (0x80 <= read && read <= 0xBF) {
							read = bis.read();
							if (0x80 <= read && read <= 0xBF) {
								charset = "UTF-8";
								break;
							} else
								break;
						} else
							break;
					}
				}
			}
		} catch (Exception e) {
			
		} finally {
			if (bis != null) {
				bis.close();
			}
		}
		return charset;
	}

	public static void scan(File dir,List<DbTable> entitys) throws FileNotFoundException, IOException{
		File [] files=dir.listFiles();
		for (File file : files) {
			if(file.isFile()){
				fileConfig(file,entitys);
			}else{
				scan(file,entitys);
			}
		}
	}
}
